#include "MyGame.h"
#include "InertialMatrixHelper.h"

#include "HUDManager.h"

/*
Creates a really simple scene for our game - A cube robot standing on
a floor. As the module progresses you'll see how to get the robot moving
around in a physically accurate manner, and how to stop it falling
through the floor as gravity is added to the scene.

You can completely change all of this if you want, it's your game!

*/
MyGame::MyGame()	{
	gameCamera = new CameraNode();
	gameCamera->SetPosition(Vector3(0, 450, 0));
	gameCamera->SetPitch(-45.0);
	gameCamera->SetYaw(180.0);

	Renderer::GetRenderer().SetCamera(gameCamera);

	// create the player //
	player = new PlayerClass();
	player->setPlayerId(player->getPlayerEntity()->getId());
	allEntities.push_back(player->getPlayerEntity());
	// ----------------- //


	// ------- Added by Monica ------ //
	//create the terrain
	terrain = new TerrainClass();
	player->setTerrainEntity(terrain);

	debug = true;

	//add the terrain nodes to allEntities
	for (int i = 0; i < terrain->GetTerrainEntitiesSize(); i++) {
		allEntities.push_back(terrain->GetGameEntityAt(i));
	}

	//set the entity in physics system
	// needed to update the player 
	PhysicsSystem::GetPhysicsSystem().SetPlayerEntity(player);
	PhysicsSystem::GetPhysicsSystem().SetPlayerPhysicsNode(player->getPlayerPhysicsNode());
	//set the terrain in physicsSystem
	PhysicsSystem::GetPhysicsSystem().SetTerrainEntity(terrain);
	// ---------------------------------------------------- //

	cube = new OBJMesh(MESHDIR"cube.obj");

	HUDManager::GetInstance()->SetPlayerHealth(player->getHealth());
	HUDManager::GetInstance()->SetPlayerXboxController(player->GetXboxController());
	HUDManager::GetInstance()->Intialise();

	//GameSound* gs = new GameSound(gameCamera);
	//gs->GenerateSound();

	// *** Add everything that creates an object in allEntities before this ***** //
	// Spawner object creation //
	buffSpawnTimer = 0;
	debuffSpawnTimer = 0;
	gameTimer = 10000;

	objectSpawner = new Spawner(terrain->getPositionLastChunck(), 100);

	// ------ Added by Monica ------- //
	objectSpawner->setTerrainEntity(terrain);
	/// ------------------------------- ///
	objectSpawner->flipAcitve();
	controller = new AIController();
	player->getSubject()->addObserver(controller);
	// ------------------------------- //
}

MyGame::~MyGame(void)	{
	delete cube;
	delete objectSpawner;
	spawnedObjectsPhysicsNodes.clear();
	CubeRobot::DeleteCube();
	HUDManager::GetInstance()->Release();
}

void MyGame::UpdateGame(float msec)
{
	if (!HUDManager::GetInstance()->GetPaused() && HUDManager::GetInstance()->GetRunning())
	{

		if (gameCamera)
		{
			gameCamera->Update(msec);

			//// --------------- Added by Monica ----------------------- //
			terrain->UpdateTerrain(msec);
			//// ---------------------------------------------------- //

			//------------------- Spawning By Borja ----------------//
			gameTimer -= msec;
			if (Window::GetKeyboard()->KeyDown(BUFF_PLAYER) && !Window::GetKeyboard()->KeyHeld(BUFF_PLAYER))
			{
				player->Buff();
				player->getSubject()->notify(player);
			}

			if (Window::GetKeyboard()->KeyDown(DEBUFF_PLAYER) && !Window::GetKeyboard()->KeyHeld(DEBUFF_PLAYER))
			{
				player->Debuff();
				player->getSubject()->notify(player);
			}

			controller->Update(msec);
			buffSpawnTimer += msec;
			debuffSpawnTimer += msec;
			if (objectSpawner->isActive() && debuffSpawnTimer >= controller->getCurrentMaliciousTickRate())
			{
				allEntities.push_back(objectSpawner->spawn(2));
				// -------- Added by Monica ---------------- //
				//Purpose: so that the objects modify their velocity depending on the terrain collision
				//we store their physics Nodes (for easier access) and their ids so we can retrieve them from allEntities
				spawnedObjectsPhysicsNodes.push_back(&allEntities.at(allEntities.size() - 1)->GetPhysicsNode());
				spawnedObjectsIds.push_back(allEntities.at(allEntities.size() - 1)->getId());
				// --------------------------------------- //
				debuffSpawnTimer = 0;
			}
			if (objectSpawner->isActive() && buffSpawnTimer >= controller->getCurrentGoodTickRate())
			{
				allEntities.push_back(objectSpawner->spawn(1));
				// -------- Added by Monica ---------------- //
				//Purpose: so that the objects modify their velocity depending on the terrain collision
				//we store their physics Nodes (for easier access) and their ids so we can retrieve them from allEntities
				spawnedObjectsPhysicsNodes.push_back(&allEntities.at(allEntities.size() - 1)->GetPhysicsNode());
				spawnedObjectsIds.push_back(allEntities.at(allEntities.size() - 1)->getId());
				// --------------------------------------- //
				buffSpawnTimer = 0;
			}

			//--------------------------------------------------//

			// ----- Modified by Monica -------- //
			for (int i = 0; i < allEntities.size(); i) {
				allEntities.at(i)->Update(msec);
				if (allEntities.at(i)->GetPhysicsNode().isToDespawn()) {
					objectSpawner->despawn(allEntities.at(i));

					// -------- Added by Monica ------------ //
					//delete the element with the same id as the one deleted from allEntities from spawnedObjectsIds
					int index = getIndexOfElementFromSpawnedObjectsIds(allEntities.at(i)->getId());
					if (index == -1) {
						break;
					}
					//now delete the physics node of the element
					spawnedObjectsPhysicsNodes.erase(spawnedObjectsPhysicsNodes.begin() + index);
					// ------------------------------------- //

					//erase the element from allEntities
					allEntities.erase(allEntities.begin() + i);
					// ----------------------------------- //

				}
				else
				{
					++i;
				}
			}

			// ------------ Added by Monica -------------------------- //
			//modify the velocity of the objects accroding to the velocity of terrain
			if (PlayerClass::getHasCollidedForObjects() == true) {
				setLinearVelocityOfSpawnedObjects(Vector3(0, 0, 0));
			}
			else {
				setLinearVelocityOfSpawnedObjects(Vector3(0, 0, TerrainClass::LINEAR_VELOCITY_Z));
			}
			// -------------------------------------------------------- //

			//SoundSystem::GetSoundSystem()->Update(msec);
		}
	}

	//SoundSystem::GetSoundSystem()->Update(msec);

	HUDManager::GetInstance()->SetPlayerHealth(player->getHealth());
	HUDManager::GetInstance()->Update(msec);
	if (HUDManager::GetInstance()->GetReset_HUD())
	{
		controller->Reset();
		terrain->restartTerrain();
		HUDManager::GetInstance()->Reset();
	}
}


// ---------- Added by Monica ---------------- //
int MyGame::getIndexOfElementFromSpawnedObjectsIds(int id) {
	for (int i = 0; i < spawnedObjectsIds.size(); i++) {
		if (id == spawnedObjectsIds.at(i)) {
			//erase the element
			spawnedObjectsIds.erase(spawnedObjectsIds.begin() + i);
			return i;
		}
	}
	//in case of error return -1
	return -1;
}

void MyGame::setLinearVelocityOfSpawnedObjects(Vector3 velocity) {
	for (int i = 0; i < spawnedObjectsPhysicsNodes.size(); i++) {
		spawnedObjectsPhysicsNodes.at(i)->SetLinearVelocity(velocity);
	}
}

void MyGame::restartSpawnObject() {
	//erase the objects from allEntities
	for (int i = 0; i < spawnedObjectsIds.size(); i++) {
		//despawn it
		//costly but it is done only at restart
		int id = getIdInAllEntities(spawnedObjectsIds.at(i));
		objectSpawner->despawn(allEntities.at(id));
		allEntities.erase(allEntities.begin() + id);
	}
	//clear the two lists
	spawnedObjectsIds.clear();
	spawnedObjectsPhysicsNodes.clear();
}

int MyGame::getIdInAllEntities(int id) {
	for (int i = 0; i < allEntities.size(); i++) {
		if (allEntities.at(i)->getId() == id) {
			return i;
		}
	}
	return -1;
}
/// ----------------------------------------------- //
